package com.ruoyi.common.sdk.ssh;

import com.jcraft.jsch.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
public class JschClient {


    private final String port;

    private final String ip;

    private final String username;

    private final String password;


    // 私有构造函数，禁止外部直接实例化
    private JschClient(Builder builder) {
        this.port = builder.port;
        this.ip = builder.ip;
        this.username = builder.username;
        this.password = builder.password;
    }

    // 内部静态Builder类
    public static class Builder {
        private String port;
        private String ip;
        private String username;
        private String password;

        public Builder() {
        }

        public Builder port(String port) {
            this.port = port;
            return this;
        }

        public Builder ip(String ip) {
            this.ip = ip;
            return this;
        }

        public Builder username(String username) {
            this.username = username;
            return this;
        }

        public Builder password(String password) {
            this.password = password;
            return this;
        }

        public JschClient build() {
            return new JschClient(this);
        }
    }

    //jsch创建连接
    public Session getJSchSession() {
        JSch jSch = new JSch();
        Session session = null;
        try {
            //创建连接
            session = jSch.getSession(username, ip, Integer.valueOf(port));
            session.setPassword(password);
            //是否使用密钥登录，一般默认为no
            session.setConfig("StrictHostKeyChecking", "no");
            //启用连接
            session.connect();
            System.out.println("==============服务器连接成功==============");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("==============服务器连接失败==============");
        }
        return session;
    }

    //jsch关闭连接
    public void closeJSchSession(Session session) {
        if (session != null) {
            try {
                session.disconnect();
                System.out.println("===========服务器连接关闭成功===========");
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("===========服务器连接关闭失败===========");
            }
        }
    }

    //用来执行命令
    //Session表示传递连接对话,commands表示传递命令集合
    public String getCmdResult(Session session, String command) {
        //用来存放命令的返回值
        StringBuffer cmdResult = new StringBuffer();
        Channel channel = null;
        try {
            //创建执行通道
            channel = session.openChannel("exec");
            /**设置命令*/
            ((ChannelExec) channel).setCommand(command);
            //连接通道
            channel.connect();
            //读取通道的输出
            InputStream in = channel.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            //存放命令的执行结果，如果结果有很多行，则每次line的值不同
            String line;
            //lines用来拼接line结果
            StringBuffer lines = new StringBuffer();
            while ((line = reader.readLine()) != null) {
                //去除头尾的空格
                line.trim();
                lines = lines.append(line);
            }
            //如果命令执行没有返回值，则直接输出没有返回值
            if (String.valueOf(lines).equals("")) {
                cmdResult.append("命令[" + command + "]执行成功,但没有返回值");
            } else {
                //否则将每行返回直接存入到list中
                cmdResult.append(String.valueOf(lines));

            }
            log.debug(cmdResult.toString());
            reader.close();
            channel.disconnect();
        } catch (JSchException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return cmdResult.toString();
    }

    /**
     * 下载并压缩远程目录下的所有文件
     *
     * @param session       已经建立的SSH会话。
     * @param remoteDirPath 远程服务器上的目录路径。
     * @param localDirPath  本地保存文件的目录路径。
     * @param zipFilePath   ZIP文件的本地路径。
     * @throws JSchException
     * @throws SftpException
     * @throws IOException
     */
    public void downloadAndZipRemoteDirectory(Session session, String remoteDirPath, String localDirPath, String zipFilePath) throws JSchException, SftpException, IOException {
        ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp");
        sftp.connect();

        // 创建本地目录
        File localDir = new File(localDirPath);
        if (!localDir.exists()) {
            localDir.mkdirs();
        }

        // 获取远程目录列表
        Vector<ChannelSftp.LsEntry> entries = sftp.ls(remoteDirPath);

        List<File> filesToZip = new ArrayList<>();
        recursiveDownload(sftp, remoteDirPath, localDirPath, filesToZip);

        // 确保目标目录存在
        File zipFile = new File(zipFilePath);
        File parentDir = zipFile.getParentFile();
        if (!parentDir.exists()) {
            parentDir.mkdirs();
        }

        // 压缩文件
        try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFilePath))) {
            for (File file : filesToZip) {
                ZipEntry zipEntry = new ZipEntry(file.getAbsolutePath().substring(localDir.getAbsolutePath().length() + 1));
                zipOut.putNextEntry(zipEntry);
                System.out.println("Adding file to ZIP: " + file.getAbsolutePath());

                try (InputStream is = new FileInputStream(file)) {
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = is.read(buffer)) > 0) {
                        zipOut.write(buffer, 0, length);
                    }
                }

                zipOut.closeEntry();
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("Failed to create ZIP file: " + e.getMessage());
        }

        // 断开SFTP连接
        sftp.disconnect();
    }

    /**
     * 递归下载远程目录下的所有文件
     *
     * @param sftp          SFTP通道
     * @param remoteDirPath 远程目录路径
     * @param localDirPath  本地目录路径
     * @param filesToZip    存储待压缩文件的列表
     * @throws SftpException
     * @throws IOException
     */
    private void recursiveDownload(ChannelSftp sftp, String remoteDirPath, String localDirPath, List<File> filesToZip) throws SftpException, IOException {
        Vector<ChannelSftp.LsEntry> entries = sftp.ls(remoteDirPath);

        for (ChannelSftp.LsEntry entry : entries) {
            String fileName = entry.getFilename();
            if (!fileName.equals(".") && !fileName.equals("..")) {
                String remoteFilePath = remoteDirPath + "/" + fileName;
                File localFile = new File(localDirPath, fileName);

                if (entry.getAttrs().isDir()) {
                    // 如果是目录，递归处理
                    if (!localFile.exists()) {
                        localFile.mkdir();
                    }
                    recursiveDownload(sftp, remoteFilePath, localFile.getAbsolutePath(), filesToZip);
                } else {
                    // 如果是文件，下载并添加到待压缩文件列表
                    sftp.get(remoteFilePath, localFile.getAbsolutePath());
                    filesToZip.add(localFile);
                }
            }
        }
    }


}
